﻿#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <stdlib.h>
/*深入理解指针5*/

/*
* sizeof 计算变量所占内存内存空间⼤⼩的，单位是
字节，如果操作数是类型的话，计算的是使⽤类型创建的变量所占内存空间的⼤⼩。
sizeof 只关注占⽤内存空间的⼤⼩，不在乎内存中存放什么数据
*/

//int main()
//{
//	int a = 10;
//	printf("%d\n", sizeof(a));
//	printf("%d\n", sizeof a);
//	printf("%d\n", sizeof(int));
//	return 0;
//}

/*
* strlen 是C语⾔库函数，功能是求字符串⻓度
统计的是从 strlen 函数的参数 str 中这个地址开始向后， \0 之前字符串中字符的个数。
strlen 函数会⼀直向后找 \0 字符，直到找到为⽌，所以可能存在越界查找
*/

//int main()
//{
//	char arr1[3] = { 'a', 'b', 'c' };
//	char arr2[] = "abc";
//	printf("%d\n", strlen(arr1));
//	printf("%d\n", strlen(arr2));
//	printf("%d\n", sizeof(arr1));
//	printf("%d\n", sizeof(arr2));
//	return 0;
//}


/*
* sizeof和strlen的对⽐
sizeof 
1. sizeof是操作符
2. sizeof计算操作数所占内存的
⼤⼩，单位是字节
3. 不关注内存中存放什么数据
strlen
1. strlen是库函数，使⽤需要包含头⽂件 string.h 
2. srtlen是求字符串⻓度的，统计的是 \0 之前字符的个数
3. 关注内存中是否有 \0 ，如果没有 \0 ，就会持续往后找，可能
会越界
*/



/*数组指针笔试题解析*/


//一维数组
//int main()
//{
//	int a[] = { 1,2,3,4 };
//	printf("int a[] = { 1,2,3,4 };\n");
//	printf("sizeof(a)\t\t=%zd\n", sizeof(a));//数组名在sizeof(a)/&a的情况下代表整个数组
//	printf("sizeof(a + 0)\t\t=%zd\n", sizeof(a + 0));//由于+0有指针步进所以就转变为了第零个数据的地址
//	printf("sizeof(*a)\t\t=%zd\n", sizeof(*a));//即求a[0]的字节数
//	printf("sizeof(a + 1)\t\t=%zd\n", sizeof(a + 1));//由于+1有指针步进所以就转变为了第零个数据的地址
//	printf("sizeof(a[1])\t\t=%zd\n", sizeof(a[1]));//即求a[1]的字节数
//	printf("sizeof(&a)\t\t=%zd\n", sizeof(&a));//即求整个数组起始地址的字节数
//	printf("sizeof(*&a)\t\t=%zd\n", sizeof(*&a));//&a代表取整个数组的地址，然后解引用则代表指向整个数组
//	printf("sizeof(&a + 1)\t\t=%zd\n", sizeof(&a + 1));//虽然&a + 1步进是一个数组的长度，但是它本身是一个指针所以依据平台确定它的字节数
//	printf("sizeof(&a[0])\t\t=%zd\n", sizeof(&a[0]));//a[0]的地址是一个指针，字节数依据平台而定
//	printf("sizeof(&a[0] + 1)\t=%zd\n", sizeof(&a[0] + 1));//&a[0]是一个指针，字节数依据平台而定,+1/-1步进为一个int的长度
//	return 0;
//}

//字符数组

//int main()
//{
//	//sizeof只关心占据的存储空间
//	//char arr[] = { 'a','b','c','d','e','f' };
//	//printf("%zd\n", sizeof(arr));//6
//	//printf("%zd\n", sizeof(arr + 0));//x64/8
//	//printf("%zd\n", sizeof(*arr));//1
//	//printf("%zd\n", sizeof(arr[1]));//1
//	//printf("%zd\n", sizeof(&arr));//x64/8
//	//printf("%zd\n", sizeof(&arr + 1));//x64/8
//	//printf("%zd\n", sizeof(&arr[0] + 1));//x64/8
//
//	//strlen只关心数组中'\0'字符之前有几个数组//不能传入不是地址的参数
//	//char arr[] = { 'a','b','c','d','e','f' };
//	////char brr[] = { '\0'};//取消注释和不取消对比可以看到以下的数据是随机的相互之间有关联因为毕竟是一个数组
//	//printf("%d\n", strlen(arr));//随机，因为arr数组末尾没有'\0'
//	//printf("%d\n", strlen(arr + 0));//随机，因为arr数组末尾没有'\0'
//	////printf("%d\n", strlen(*arr));//报错//因为*arr==arr[0]不是一个地址
//	////printf("%d\n", strlen(arr[1]));//报错//因为arr[1]不是一个地址
//	//printf("%d\n", strlen(&arr));//随机，因为arr数组末尾没有'\0'
//	//printf("%d\n", strlen(&arr + 1));//随机，因为arr后面的内存空间是未知的
//	//printf("%d\n", strlen(&arr[0] + 1));//随机，因为arr数组末尾没有'\0'
//
//
//	//char arr[] = "abcdef";
//	//printf("%zd\n", sizeof(arr));//7
//	//printf("%zd\n", sizeof(arr + 0));//x64/8
//	//printf("%zd\n", sizeof(*arr));//1
//	//printf("%zd\n", sizeof(arr[1]));//1
//	//printf("%zd\n", sizeof(&arr));//x64/8
//	//printf("%zd\n", sizeof(&arr + 1));//x64/8
//	//printf("%zd\n", sizeof(&arr[0] + 1));//x64/8
//
//	//char arr[] = "abcdef";
//	////char brr[] = { '\0' };
//	//printf("%d\n", strlen(arr));//6
//	//printf("%d\n", strlen(arr + 0));//6
//	////printf("%d\n", strlen(*arr));//报错
//	////printf("%d\n", strlen(arr[1]));//报错
//	//printf("%d\n", strlen(&arr));//6
//	//printf("%d\n", strlen(&arr + 1));//随机
//	//printf("%d\n", strlen(&arr[0] + 1));//5
//
//	//char* p = "abcdef";
//	//printf("%zd\n", sizeof(p));//指针变量//x64/8
//	//printf("%zd\n", sizeof(p + 1));//指针变量//x64/8
//	//printf("%zd\n", sizeof(*p));//p指向的是"abcdef"的首字符故*p='a'//1
//	//printf("%zd p[1]=%c\n", sizeof(p[1]), p[1]);//*(p+1)='b'//1
//	//printf("%zd\n", sizeof(&p));//指针变量//x64/8
//	//printf("%zd\n", sizeof(&p + 1));//指针变量//x64/8//步进也是1因为p不是数组
//	//printf("%zd\n", sizeof(&p[0] + 1));//'b'常量所在的地址//指针变量//x64/8
//	
//	////strlen只负责依据传入的地址往后检查其它的一概不管
//	//char* p = "abcdef";
//
//	//printf("%d p=%p\n", strlen(p), p);//6
//	//printf("%d\n", strlen(p + 1));//5
//	////printf("%d\n", strlen(*p));//报错
//	////printf("%d\n", strlen(p[0]));//报错
//	//printf("%d &p=%p\n", strlen(&p), &p);//&p是char**型的二级指针，结果是随机的
//	//printf("%d &p + 1=%p\n", strlen(&p + 1), &p + 1);//&p + 1也是char**型的二级指针，结果是随机的
//	//printf("%d\n", strlen(&p[0] + 1));//5
//	return 0;
//}


//二维数组

/*
* 
1. sizeof(数组名)，这⾥的数组名表⽰整个数组，计算的是整个数组的⼤⼩。
2. &数组名，这⾥的数组名表⽰整个数组，取出的是整个数组的地址。
3. 除此之外所有的数组名都表⽰⾸元素的地址。
*/


//可以根据一维数组的概念进行理解例如此题中// 数组名			数组元素
//数组a为二维数组，则a为数组名即相对a[0]//	a		->	a[0]
//而a[0]为一维数组的数组名即相对a[0][0]//	a[0]	->	a[0][0]
//								  /////////////////////////////
//								  //  *(a + 1)  ==  a[1]
// //							  //  a + 1		==  &a[1]
//								  //  *(a[0] + 1)  ==  a[0][1]
// //							  //  a[0] + 1		==  &a[0][1]
//int main()
//{
//	int a[3][4] = { 0 };
//	printf("int a[3][4] = { 0 };\n");
//	printf("sizeof(a)\t\t=%zd\n", sizeof(a));//整个数组的大小//3*4*4=48
//	printf("sizeof(a[0][0])\t\t=%zd\n", sizeof(a[0][0]));//int 的大小//4
//	printf("sizeof(a[0])\t\t=%zd\n", sizeof(a[0]));//16//一维数组的大小//此时a[0]就相当于一维数组的数组名
//	printf("sizeof(a[0] + 1)\t\t=%zd\n", sizeof(a[0] + 1));//x64/8
//	printf("sizeof(*(a[0] + 1))\t\t=%zd\n", sizeof(*(a[0] + 1)));//int 的大小//4
//	printf("sizeof(a + 1)\t\t=%zd\n", sizeof(a + 1));//x64/8//等效于&a[1]
//	printf("sizeof(*(a + 1))\t\t=%zd\n", sizeof(*(a + 1)));//一维数组的大小//4*4=16//等效于a[1]
//	printf("sizeof(&a[0] + 1)\t\t=%zd\n", sizeof(&a[0] + 1));//x64/8//指向a[1]
//	printf("&a[0] + 1=%p;a[1]=%p\n", &a[0] + 1, a[1]);
//	printf("sizeof(*(&a[0] + 1))\t\t=%zd\n", sizeof(*(&a[0] + 1)));//一维数组的大小//4*4=16
//	printf("sizeof(*a)\t\t=%zd\n", sizeof(*a));//一维数组的大小//4*4=16//可理解为：*(a+0)==a[0]
//	printf("sizeof(a[3])\t\t=%zd\n", sizeof(a[3]));//一维数组的大小//4*4=16//可理解为：*(a+3)==a[3]
//	return 0;
//}


/*
* 指针运算笔试题解析
*/

//题目1
//int main()
//{
//
//		int a[5] = { 1, 2, 3, 4, 5 };
//		int* ptr = (int*)(&a + 1);//指向数组a后面的int [5]数组的第一个元素
//		printf("%d,%d", *(a + 1), *(ptr - 1));//2   5
//		return 0;
//}


//题目2

//在X86环境下
//假设结构体的⼤⼩是20个字节
//程序输出的结果是啥？
//struct Test
//{
//	int Num;
//	char* pcName;
//	short sDate;
//	char cha[2];
//	short sBa[4];
//}*p = (struct Test*)0x100000;
//int main()
//{
//	/*
//	* %p对变量进行的格式化是将变量值以16进制打印，并在前面添加0x
//不是所谓的打印变量地址！
//而 printf 只是格式化输出，是把某个变量的值进行格式化（写成某种形式），然后输出，并不存在获取变量地址的功能。
//所以不是%p可以打印整型变量但是会进行格式化
//	*/
//	printf("%p\n", p + 0x1);//0x100014//步进一个结构体
//	printf("%p\n", (unsigned long)p + 0x1);//0x100001//因为(unsigned long)p已经不是地址了所以会输出格式化后的数据
//	printf("%p\n", (unsigned int*)p + 0x1);//0x100004//步进一个结构体unsigned int数据
//	return 0;
//}

//题目3
//int main()
//{
//	int a[3][2] = { (0, 1), (2, 3), (4, 5) };
//	//逗号表达式的值等于最右边的值
//	//由于使用的是逗号表达式初始化所以初始化的实际效果是
//	//int a[3][2] = { {1,3},{5,0},{0,0}};
//	int* p;
//	
//	p = a[0];
//	printf("%p\n", p);
//	printf("%p\n", &p[0]);
//	printf("%p\n", &a[0][0]);
//	printf("%d\n", p[0]);
//	return 0;
//}

//题目4
//假设环境是x86环境，程序输出的结果是啥？
//int main()
//{
//	int a[5][5];
//	int(*p)[4];
//	p = a;//a[n]没步进1，则&p[n][2] - &a[n][2]就多相差1
//	printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);//两者一样，一个格式化一个没格式化
//	return 0;
//}

//题目5

//int main()
//{
//	int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
//	int b[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
//	int *ptr1 = (int *)(&aa + 1);//
//	int *ptr2 = (int *)(*(aa + 1));//等价于*(aa + 1)==aa[1]
//	printf( "%d,%d", *(ptr1 - 1), *(ptr2 - 1));
//	return 0;
//}

//题⽬6

//int main()
//{
//	char* a[] = { "work","at","alibaba" };
//	//char(*pa)[3] = a;
//	char** pa = a;//因为pa是char*类型而不是char [3]类型的指针所以其步进是以数组元素为步进单元
//	pa++;
//	printf("%s\n", *pa);
//	return 0;
//}

//题目7

//int main()
//{
//	char* c[] = { "ENTER","NEW","POINT","FIRST" };
//	char** cp[] = { c + 3,c + 2,c + 1,c };//->{"FIRST","POINT","NEW","ENTER"}
//	char*** cpp = cp;//->cp
//
//	printf("%s\n", **++cpp);//cpp->cp+1
//	//++cpp->&cp[1]->"POINT"
//
//	printf("%s\n", *-- * ++cpp + 3);//cpp->cp+2
//	//cpp	->++cpp	->* ++cpp ->-- * ++cpp ->*-- * ++cpp ->*-- * ++cpp+3
//	//cp+1	->cp+2  ->	c + 1 ->c + 0	   ->c[0]		 ->	"ER"
//
//	printf("%s\n", *cpp[-2] + 3);////cpp->cp+2///
//	//cpp	->cpp[-2]==**(cpp-2)		->*cpp[-2] + 3 
//	//cp+2	->**(cp)==*(c + 3)=="FIRST"	->"ST"
//
//	printf("%s\n", cpp[-1][-1] + 1);//cpp->cp+2
//	//cpp	->cpp[-1][-1]==*(*(cpp-1)	-1)								->cpp[-1][-1] + 1 
//	//cp+2	->*(*(cp+2-1)-1)==*(*(cp+1)-1)==*((c+2)-1)==*(c+1)=="NEW"	->"EW"
//	return 0;
//}



//练习

int main()
{
	int a[][3] = { {1,2,3},{4,5,7} };
	printf("%p\n", &a[0]);
	printf("%p\n", a);
	printf("%zd\n", sizeof(a));
	printf("%p\n", &a);
	printf("%zd\n\n", sizeof(&a));

	printf("%p\n", &a[0][0]);
	printf("%p\n", a[0]);
	printf("%zd\n", sizeof(a[0]));
	printf("%p\n", &a[0]);
	printf("%zd\n\n", sizeof(&a[0]));

	printf("%zd\n", sizeof(a[0]));
	printf("%zd\n", sizeof(*(a + 0)));
	printf("%zd\n", sizeof(&a[0]));
	printf("%zd\n\n", sizeof(a + 0));

	printf("%zd\n", sizeof(a[0][0]));
	printf("%zd\n", sizeof(*(a[0] + 0)));
	printf("%zd\n", sizeof(&a[0][0]));
	printf("%zd\n\n", sizeof(a[0] + 0));

	printf("%p\n", a);
	printf("%p\n", a + 1);
	printf("%p\n", &a[0] + 1);
	printf("%p\n\n", &a + 1);
	
	printf("%p\n", a[0]);
	printf("%p\n", a[0] + 1);
	printf("%p\n", &a[0][0] + 1);
	printf("%p\n\n", &a[0] + 1);
	return 0;
}